package com.ac.scum.log.task.face.log.counsumer;

import com.ac.common.core.id.SnowflakeIdGenerator;
import com.ac.scum.config.bean.ServerFTPFileConfigAdmin;
import com.ac.scum.log.bean.Player;
import com.ac.scum.log.bean.ServerFTPLogDiscard;
import com.ac.scum.log.task.cons.TaskConst;
import com.ac.scum.log.task.mapper.config.ServerFTPFileConfigAdminMapper;
import com.ac.scum.log.task.mapper.log.ServerFTPLogDiscardMapper;
import com.ac.scum.log.task.mapper.player.PlayerMapper;
import com.ac.scum.log.task.utils.queue.FTPQueueItem;
import com.ac.scum.log.task.utils.spring.SpringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import io.micrometer.common.util.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public interface LogConsumer {
    String NO_NAME_PLAYER_HASH_REDIS_KEY = "NO_NAME_PLAYER";

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd-HH.mm.ss");
    Pattern pattern = Pattern.compile("\\d{17}");
    Cache<Long, Player> PLAYER_CACHE = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();

    @Data
    @AllArgsConstructor
    class GameLogLine {
        private String lineContext;
        private Long startByte;
        private Long endByte;
    }

    default void addPlayer(Player player) {
        checkAndCommitNotNamePlayer(player);
        SpringUtils.getBean(PlayerMapper.class).insertAndJoinServer(player);
    }

    default void writeAllPlayerInfoInCache(FTPQueueItem ftpQueueItem) {
        Matcher matcher = pattern.matcher(ftpQueueItem.getContext());
        List<Long> steamIds = Lists.newArrayList();
        while (matcher.find()) {
            steamIds.add(Long.parseLong(matcher.group()));
        }
        SpringUtils.getBean(PlayerMapper.class).findPlayerByServerIdAndSteamIds(ftpQueueItem.getServer().getId(), steamIds).forEach(item -> PLAYER_CACHE.put(item.getSteamId(), item));
    }

    default String getSpawnItemCodeCnName(String code) {
        if (code == null || StringUtils.isBlank(code)) {
            return code;
        }
        Object cnName = SpringUtils.getBean(StringRedisTemplate.class).opsForHash().get(TaskConst.LOG_CONVERT_KEY_MAP_CACHE, code.toLowerCase());
        return cnName != null ? cnName.toString() : code;
    }

    default Player getOrInsertPlayerServer(Long steamId, Long serverId, String name) {
        if (steamId< 70000000000000000L ) {
            throw new RuntimeException("SteamID 不正确，规避新增。");
        }
        Player player = PLAYER_CACHE.getIfPresent(steamId);
        if (player == null) {
            player = new Player();
            player.setName(name);
            player.setId(SpringUtils.getBean(SnowflakeIdGenerator.class).nextId());
            player.setServerId(serverId);
            player.setSteamId(steamId);
            PLAYER_CACHE.put(steamId, player);
            player.setJoinTime(LocalDateTime.now());
            addPlayer(player);
        }
        if (StringUtils.isBlank(player.getName()) && StringUtils.isNotBlank(name)) {
            checkAndCommitPlayerName(serverId, steamId, name);
        }
        return player;
    }

    default Player getOrInsertPlayerServer(Long steamId, Long serverId, String name, String accountNumber) {
        if (steamId< 70000000000000000L ) {
            throw new RuntimeException("SteamID 不正确，规避新增。");
        }

        Player player = PLAYER_CACHE.getIfPresent(steamId);
        if (player == null) {
            player = new Player();
            player.setName(name);
            player.setId(SpringUtils.getBean(SnowflakeIdGenerator.class).nextId());
            player.setServerId(serverId);
            player.setSteamId(steamId);
            player.setAccountNumber(accountNumber);
            PLAYER_CACHE.put(steamId, player);
            player.setJoinTime(LocalDateTime.now());
            addPlayer(player);
        }
        if ((StringUtils.isBlank(player.getName()) && StringUtils.isNotBlank(name)) || (StringUtils.isBlank(player.getAccountNumber()) && StringUtils.isNotBlank(accountNumber))) {
            checkAndCommitPlayerName(serverId, steamId, name, accountNumber);
        }
        return player;
    }

    private void checkAndCommitNotNamePlayer(Player player) {
        if (StringUtils.isBlank(player.getName())) {
            String key = player.getServerId().toString() + player.getSteamId().toString();
            SpringUtils.getBean(StringRedisTemplate.class).opsForHash().put(NO_NAME_PLAYER_HASH_REDIS_KEY, key, "N/A");
        }
    }

    default void checkAndCommitPlayerName(Long serverId, Long steamId, String name) {
        StringRedisTemplate stringRedisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
        String key = serverId.toString() + steamId.toString();
        SpringUtils.getBean(PlayerMapper.class).updateServerPlayerName(serverId, steamId, name);
        String result = (String) stringRedisTemplate.opsForHash().get(NO_NAME_PLAYER_HASH_REDIS_KEY, key);
        if (result != null && result.equals("N/A")) {
            stringRedisTemplate.opsForHash().delete(NO_NAME_PLAYER_HASH_REDIS_KEY, key);
        }
    }

    default void checkAndCommitPlayerName(Long serverId, Long steamId, String name, String accountNumber) {
        StringRedisTemplate stringRedisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
        String key = serverId.toString() + steamId.toString();
        SpringUtils.getBean(PlayerMapper.class).updateServerPlayerNameAndAccountNumber(serverId, steamId, name, accountNumber);
        String result = (String) stringRedisTemplate.opsForHash().get(NO_NAME_PLAYER_HASH_REDIS_KEY, key);
        if (result != null && result.equals("N/A")) {
            stringRedisTemplate.opsForHash().delete(NO_NAME_PLAYER_HASH_REDIS_KEY, key);
        }
    }

    default void commitDiscardLog(FTPQueueItem ftpQueueItem, GameLogLine gameLogLine, String msg) {
        ServerFTPLogDiscard serverFTPLogDiscard = new ServerFTPLogDiscard();
        serverFTPLogDiscard.setFileId(ftpQueueItem.getFileId());
        serverFTPLogDiscard.setServerId(ftpQueueItem.getServer().getId());
        serverFTPLogDiscard.setMsg(msg);
        serverFTPLogDiscard.setContext(gameLogLine.getLineContext());
        serverFTPLogDiscard.setStartBit(gameLogLine.getStartByte());
        serverFTPLogDiscard.setEndBit(gameLogLine.getEndByte());
        SpringUtils.getBean(ServerFTPLogDiscardMapper.class).insert(serverFTPLogDiscard);
    }

    default List<GameLogLine> splitLine(FTPQueueItem ftpQueueItem) {
        writeAllPlayerInfoInCache(ftpQueueItem);
        List<GameLogLine> gameLogLines = Lists.newArrayList();
        Long offset = ftpQueueItem.getOffset();
        for (String line : ftpQueueItem.getContext().split("\\n")) {
            //有效验证
            if (StringUtils.isBlank(line) || line.contains("Game version")) {
                offset += line.getBytes(StandardCharsets.UTF_16LE).length;
            } else {
                int length = line.getBytes(StandardCharsets.UTF_16LE).length;
                long end = offset + length;
                gameLogLines.add(new GameLogLine(line, offset, end));
                offset += length;
            }
        }
        return gameLogLines;
    }

    default LocalDateTime parseTimestamp(String dateString, FTPQueueItem ftpQueueItem) {
        return LocalDateTime.parse(dateString, formatter).plusMinutes(ftpQueueItem.getServerFTP().getTimeOffset());

    }

    void consumerLog(FTPQueueItem ftpQueueItem);

    default List<Long> getRobotSteamIds(FTPQueueItem ftpQueueItem) {
        return SpringUtils.getBean(ServerFTPFileConfigAdminMapper.class).selectList(Wrappers.lambdaQuery(ServerFTPFileConfigAdmin.class).eq(ServerFTPFileConfigAdmin::getServerId, ftpQueueItem.getServer().getId()).eq(ServerFTPFileConfigAdmin::getType, 3)).stream().map(ServerFTPFileConfigAdmin::getSteamId).toList();
    }

}
